package com.slb.slnwp.common.utils;

import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class DataEncrypUtil {

    private static final String IP = "10.1.58.70:15443";// 请求的ip地址
    private static final String dekKeyCode = "b4052a561f634e91b3fc51c7811b691f";// 有效期至2033-11-28 17:03:39
    private static final String BASIC_AUTH = "c2xud3A6U2xud3BAMjAyMw=="; // Base64 编码的 slnwp:Slnwp@2023


    /**
     * 对数据进行SM4加密方法(传入参数类型为：对象)
     * 用于数据存储机密性保护：用户，访问日志等
     *
     * @param enobj
     * @return
     */
    public static Object encryptDataObj(Object enobj) {
        String extractData = null;
        try {
            // 将传入的对象的值进行base64编码
            String enUser = convertObjectToJson(enobj);
            // 拼接加密方法接口调用的URL
            String encryptUrl = "https://" + IP + "/api/v1/cipher/json/encrypt";
            String encryptRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4/ECB/PKCS7Padding\",\n" +
                    "\t\"data\":" + enUser + "\n" +
                    "}";
            // 调用数据加密方法加密
            String encryptResponseJson = sendPostRequest(encryptUrl, encryptRequestBody);
            // 将结果转化为对象
            System.out.println("加密结果=" + encryptResponseJson);
            // 提取需要解密的内容，从返回数据中获取encData字段结果
            extractData = extractData(encryptResponseJson);
            //encoderUser = new ObjectMapper().readValue(extractData, SnpDutyStaff.class);
            System.out.println("转换结果=" + extractData);
        } catch (Exception e) {
            log.error("数据加密失败...", e);
        }

        return extractData;
    }

    /**
     * 对数据进行SM4加密方法(传入参数类型为：json)
     * 用于数据存储机密性保护：用户，访问日志等
     *
     * @param jsonData
     * @return
     */
    public static String encryptDataJson(String jsonData) {
        String extractData = null;
        try {
            // 将传入的json的值进行base64编码
            String enUser = encodeJsonValues(jsonData);
            String encryptUrl = "https://" + IP + "/api/v1/cipher/json/encrypt";
            String encryptRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4/ECB/PKCS7Padding\",\n" +
                    "\t\"data\":" + enUser + "\n" +
                    "}";
            // 调用数据加密接口进行数据加密
            String encryptResponseJson = sendPostRequest(encryptUrl, encryptRequestBody);
            // 输入加密结果
            System.out.println("加密结果=" + encryptResponseJson);
            // 直接转换为SnpDutyStaff（后续如需修改，改成通用即可，或者返回String，在需要加密的地方再转换）
            extractData = extractData(encryptResponseJson);
            System.out.println("转换结果=" + extractData);

        } catch (Exception e) {
            log.error("数据加密失败...", e);
        }

        return extractData;
    }

    /**
     * 计算传入数据的完整性验证MAC码
     * 验证方式:SM4_MAC方式
     * 用于数据的完整性验证，如：用户，访问日志等
     *
     * @param macData
     * @return
     */
    public static String cipherMac(String macData) {
        String resultJson = null;
        try {
            //（String类型）进行base64编码
            String baseData = encodeToBase64(macData);
            String encryptUrl = "https://" + IP + "/api/v1/cipher/mac";
            String encryptRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4_MAC\",\n" +
                    "\t\"data\":\"" + baseData + "\",\n"+
                    "}";
            // 获取完整性验证MAC码
            String cipherMacResponseJson = sendPostRequest(encryptUrl, encryptRequestBody);
            // 获取结果
            System.out.println("计算MAC=" + cipherMacResponseJson);
            // 提取mac和iv的值
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将 JSON 字符串解析为 JsonNode 对象
                JsonNode jsonNode = objectMapper.readTree(cipherMacResponseJson);
                // 提取 mac 和 iv
                String mac = jsonNode.get("data").get("mac").asText();
                String iv = jsonNode.get("data").get("iv").asText();
                // 存储到 Map
                Map<String, String> resultMap = new HashMap<>();
                resultMap.put("mac", mac);
                resultMap.put("iv", iv);
                // 将 Map 转换为 JSON 字符串
                resultJson = objectMapper.writeValueAsString(resultMap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            log.error("数据计算MAC失败...", e);
        }

        return resultJson;
    }

    /**
     * 数据完整性验证：消息鉴别码（MAC）验证，通过mac和iv码
     * 用于数据的完整性验证，如：用户，访问日志等
     * @param verifyData 验证的数据（姓名+电话）
     * @param macValue mac码
     * @return
     */
    public static Boolean cipherMacVerify(String verifyData, String macValue) {
        Boolean verifyResult = false;
        try {
            // 使用Jackson库解析JSON字符串
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonMacValue = objectMapper.readTree(macValue);
            // 将JSON节点转换为Map
            Map<String, String> macValueMap = objectMapper.convertValue(jsonMacValue, HashMap.class);
            // 转换为base64编码
            String baseData = encodeToBase64(verifyData);
            String encryptUrl = "https://" + IP + "/api/v1/cipher/macVerify";
            String encryptRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4_MAC\",\n" +
                    "\t\"data\":\"" + baseData + "\",\n"+
                    "\t\"iv\":\"" + macValueMap.get("iv") + "\",\n"+
                    "\t\"mac\":\"" + macValueMap.get("mac") + "\",\n"+
                    "}";
            // 数据完整性验证：消息鉴别码（MAC）验证
            String cipherMacResponseJson = sendPostRequest(encryptUrl, encryptRequestBody);
            // 获取结果
            System.out.println("计算MAC=" + cipherMacResponseJson);
            try {
                // 将 JSON 字符串解析为 JsonNode 对象
                JsonNode jsonNode = objectMapper.readTree(cipherMacResponseJson);
                // 提取 验证结果verifyResult
                verifyResult = jsonNode.get("data").get("verifyResult").asBoolean();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            log.error("数据计算MAC失败...", e);
        }

        return verifyResult;
    }

    /**
     * 对数据进行SM4解密的方法(传入参数为：json，加密后的内容)
     * 用于数据存储机密性保护：用户，访问日志等，对加密的数据进行解密
     *
     * @param deoJson
     * @return
     */
    public static String decodeData(String deoJson) {
        String decodeData = null;
        try {
            // 拼接解密的调用方法url
            String extractUrl = "https://" + IP + "/api/v1/cipher/json/decrypt";
            String extractRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4/ECB/PKCS7Padding\",\n" +
                    "\t\"iv\":\"\",\n" +
                    "\t\"encData\":" + deoJson + "\n" +
                    "}";
            // 获取解密返回结果
            String encData = sendPostRequest(extractUrl, extractRequestBody);
            System.out.println("解密结果=" + encData);
            // 提取结果部分
            String extractEncData = extractEncData(encData);
            // 结果进行base64解码
            decodeData = decodeEncData(extractEncData);
        } catch (Exception e) {
            log.error("数据解密失败...", e);
        }
        return decodeData;
    }

    /**
     * 调用url接口公共方法，传入两个url路径&requestBody相关参数
     * 用于数据加密，解密，以及完整性验证的相关方法调用的公共方法
     *
     * @param endpoint  url路径
     * @param requestBody requestBody相关参数
     * @return
     * @throws Exception
     */
    private static String sendPostRequest(String endpoint, String requestBody) throws Exception {
        /*URL url = new URL(endpoint);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();*/
        // 创建url链接
        URL url = new URL(endpoint);
        HttpURLConnection connection;
        // 如果是https请求，则切换为https
        if ("https".equalsIgnoreCase(url.getProtocol())) {
            connection = (HttpsURLConnection) url.openConnection();

            // 设置 SSL 相关属性，例如信任管理器和主机名验证
            // ...
            /*if (connection instanceof HttpsURLConnection) {
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection;
                // 使用默认的主机名验证器
                httpsURLConnection.setHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier());
            }*/
            if (connection instanceof HttpsURLConnection) {
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection;

                // 创建不验证证书链的信任管理器
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return null;
                            }

                            public void checkClientTrusted(
                                    java.security.cert.X509Certificate[] certs, String authType) {
                            }

                            public void checkServerTrusted(
                                    java.security.cert.X509Certificate[] certs, String authType) {
                            }
                        }
                };

                try {
                    // 不对主机名进行验证，接受任何主机名
                    SSLContext sc = SSLContext.getInstance("SSL");
                    sc.init(null, trustAllCerts, new java.security.SecureRandom());
                    httpsURLConnection.setSSLSocketFactory(sc.getSocketFactory());
                    httpsURLConnection.setHostnameVerifier((hostname, session) -> true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            connection = (HttpURLConnection) url.openConnection();
        }

        // 设置请求头相关参数
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Authorization", "Basic " + BASIC_AUTH);
        //connection.setRequestProperty("token", getToken());
        connection.setDoOutput(true);

        try (OutputStream os = connection.getOutputStream()) {
            byte[] input = requestBody.getBytes(StandardCharsets.UTF_8);
            os.write(input, 0, input.length);
        }

        // 处理响应，根据需要进行相应的操作
        int responseCode = connection.getResponseCode();
        System.out.println("Response Code: " + responseCode);

        // 获取调用接口相关结果，并返回
        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String responseLine;
            while ((responseLine = br.readLine()) != null) {
                response.append(responseLine.trim());
            }
            return response.toString();
        } finally {
            connection.disconnect();
        }
    }

    /**
     * 对数据进行SM4解密的方法(传入参数为：对象，加密后的内容)
     * 用于数据存储机密性保护：用户，访问日志等，对加密的数据进行解密
     *
     * @param deobj
     * @return
     */
    /*public static Object decodeData(Object deobj) {
        try {
            // 创建ObjectMapper对象
            ObjectMapper objectMapper = new ObjectMapper();
            // 将对象转换为JSON字符串
            String jsonString = objectMapper.writeValueAsString(deobj);
            String extractUrl = "https://" + IP + "/api/v1/cipher/json/decrypt";
            String extractRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\",\n" +
                    "\t\"algorithmParam\":\"SM4/ECB/PKCS7Padding\",\n" +
                    "\t\"iv\":\"\",\n" +
                    "\t\"encData\":" + deobj + "\n" +
                    "}";
            String encData = sendPostRequest(extractUrl, extractRequestBody);
            System.out.println("解密结果=" + encData);
            // 直接转换为SnpDutyStaff（后续如需修改，改成通用即可，或者返回String，在需要加密的地方再转换）
            SnpDutyStaff needDecoderUser = new ObjectMapper().readValue(encData, SnpDutyStaff.class);
            // base64解码
            String decoderJson = extractAndDecodeData(needDecoderUser);
            // 直接转换为SnpDutyStaff（后续如需修改，改成通用即可，或者返回String，在需要加密的地方再转换）
            decoderUser = new ObjectMapper().readValue(decoderJson, SnpDutyStaff.class);
        } catch (Exception e) {
            log.error("数据解密失败...", e);
        }
        return decoderUser;
    }*/

    /**
     * 提取需要解密的内容，从返回数据中获取encData字段结果
     * 解密返回内容中，提取我们需要的相关内容
     *
     * @param result
     * @return
     */
    public static String extractData(String result) {
        try {
            // 将 JSON 字符串转换为 JSONObject
            JSONObject jsonObject = new JSONObject(result);
            // 提取encData字段
            JSONObject encData = jsonObject.getJSONObject("data").getJSONObject("encData");
            return encData.toString();
        } catch (Exception e) {
            log.error("json转码失败...");
            return null;
        }
    }

    /**
     * 将对象的值提取出来，进行base64编码
     * 利用反射，获取对象的值，只需要对值进行base64编码
     * 用于数据加密前。解密后的相关转换
     *
     * @param obj
     * @return
     */
    public static String convertObjectToJson(Object obj) {
        Map<String, String> objMap = new HashMap<>();

        // 使用反射获取对象的属性和值
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    // 编码转化为String，并进行base64编码
                    String key = field.getName();
                    // 不是String类型，转化为String
                    String base64Value = (value instanceof String) ?
                            Base64.getEncoder().encodeToString(((String) value).getBytes()) :
                            Base64.getEncoder().encodeToString(value.toString().getBytes());
                    objMap.put(key, base64Value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        // 将 Map 转换为 JSON 格式
        JSONObject json = new JSONObject(objMap);
        return json.toString();
    }

    /**
     * 将传入的json的值进行base64编码
     * 获取json的value，只需要对value进行base64编码
     * 用于数据加密前。解密后的相关转换
     *
     * @param jsonData
     * @return
     */
    public static String encodeJsonValues(String jsonData) {
        // 匹配 JSON 数据中的 value 部分
        Pattern pattern = Pattern.compile(":\"([^\"]*)\"");
        Matcher matcher = pattern.matcher(jsonData);

        // 替换匹配到的 value 部分
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String value = matcher.group(1);
            // 对 value 进行 Base64 编码
            String encodedValue = Base64.getEncoder().encodeToString(value.getBytes());
            matcher.appendReplacement(result, ":\"" + encodedValue + "\"");
        }
        matcher.appendTail(result);

        return result.toString();
    }

    /**
     * 将传入的value（String）进行base64编码
     * 点对点进行base64编码。对传入的数据，进行base64编码
     *
     * @param value
     * @return
     */
    private static String encodeToBase64(String value) {
        // 将字符串编码为Base64
        byte[] encodedBytes = Base64.getEncoder().encode(value.getBytes(StandardCharsets.UTF_8));

        // 将字节数组转换为字符串
        return new String(encodedBytes, StandardCharsets.UTF_8);
    }

    /**
     * 提取解密后的数据，获取结果中的data数据
     * 解密后的返回值，我们只需要其中的data数据，将其分离出来
     *
     * @param encryptedResult
     * @return
     */
    public static String extractEncData(String encryptedResult) {
        // 根据JSON结构提取encData字段的值
        JSONObject data = null;
        try {
            JSONObject jsonObject = new JSONObject(encryptedResult);

            // 提取encData字段
            data = jsonObject.getJSONObject("data");
        } catch (Exception e) {
            log.error("转json报错...");
            return null;
        }

        return data.toString();
    }

    /**
     * 对传入的json数据进行Base64解码(传入：key，value的形式)
     * 解密后的数据为Base64编码的数据，需要进行Base64解码
     * 用于解密后的数据恢复
     *
     * @param encData
     * @return
     */
    public static String decodeEncData(String encData) {
        // 将encData中的value进行Base64解码
        String[] keyValuePairs = encData.split(",");
        StringBuilder decodedData = new StringBuilder("{");

        // 遍历键值对数组,对value进行base64解码
        for (String keyValuePair : keyValuePairs) {
            String[] parts = keyValuePair.split(":");
            String key = parts[0].replace("{", "").replace("\"", "").trim();
            String value = parts[1].replace("}", "").replace("\"", "").trim();

            // Base64解码value
            String decodedValue = new String(Base64.getDecoder().decode(value));

            // 构造解码后的键值对
            decodedData.append("\"").append(key).append("\":\"").append(decodedValue).append("\",");
        }

        // 移除最后一个逗号
        decodedData.deleteCharAt(decodedData.length() - 1);
        decodedData.append("}");

        return decodedData.toString();
    }

    /**
     * 对传入的数据进行base64解码（传入：Object，对象的形式）
     * 解密后的数据为Base64编码的数据，需要进行Base64解码
     * 用于解密后的数据恢复
     *
     * @param
     * @return
     */
    public static String extractAndDecodeData(Object obj) {
        Map<String, String> objMap = new HashMap<>();

        // 使用反射获取对象的属性和值
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    // 获取value，转化为String类型，并Base64加密
                    String key = field.getName();
                    // 如果不是String类型，需转换为String类型
                    String base64Value = (value instanceof String) ?
                            new String(Base64.getDecoder().decode((String) value)) :
                            new String(Base64.getDecoder().decode(value.toString()));
                    objMap.put(key, base64Value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        // 将 Map 转换为 JSON 格式
        return objMap.toString();
    }

    /**
     * 对称密钥创建方法：SM4加密密钥
     * 用于生成加密，解密算法的对称密钥，保证数据的机密性
     *
     * @return
     */
    private String dekCreate() {
        String dekCreate = null;
        try {
            // 拼接对称密钥创建Url
            String dekCreateUrl = "https://" + IP + "/api/v1/kms/dek/createKey";
            String dekCreateRequestBody = "{\n" +
                    "\t\"algorithm\":\"SM4\",\n" +
                    "\t\"length\":128,\n" +
                    "\t\"period\":100,\n" +
                    "\t\"keyUsages\":\"0,1,2\",\n" +
                    "\t\"keyNumber\":\"1\",\n" +
                    "\t\"description\":\"SM4_128_dek01\"\n" +
                    "}";
            // 获取对称密钥
            dekCreate = sendPostRequest(dekCreateUrl, dekCreateRequestBody);
        } catch (Exception e) {
            log.error("对称密钥创建失败...", e);
        }
        return dekCreate;
    }

    /**
     * 对称密钥更新方法：SM4加密密钥
     * 用于对称密钥的更新，当对称密钥过期后，对对称密钥进行更新
     *
     * @return
     */
    private String dekUpdate() {
        String dekKeyUpdateCode = null;
        try {
            // 拼接对称密钥更新Url
            String dekUpdateUrl = "https://" + IP + "/api/v1/kms/dek/reKey";
            String dekUpdateRequestBody = "{\n" +
                    "\t\"keyCode\":\"" + dekKeyCode + "\"\n" +
                    "}";
            // 获取更新后的对称密钥
            String dekUpdateJson = sendPostRequest(dekUpdateUrl, dekUpdateRequestBody);
            dekKeyUpdateCode = parseDekKeyCodeFromResponse(dekUpdateJson);
        } catch (Exception e) {
            log.error("对称密钥更新失败...", e);
        }
        return dekKeyUpdateCode;
    }

    /**
     * 返回结果中，提取密钥keyCode
     * 从返回结果中，获取我们更新后的keyCode
     * 用于对称密钥的更新方法
     *
     * @param responseJson
     * @return
     */
    private String parseDekKeyCodeFromResponse(String responseJson) {
        // 解析对称密钥创建的响应JSON，获取密钥标识
        // 将 JSON 字符串转换为 JSONObject
        JSONObject jsonObject = new JSONObject(responseJson);

        // 提取encData字段
        JSONObject keyCode = jsonObject.getJSONObject("data").getJSONObject("keyCode");
        return keyCode.toString();
    }

}
